package com.msb.im.netty;

import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.msb.im.module.waiter.service.StoreService;
import com.msb.im.netty.handler.server.ServerChannelInitializer;
import com.msb.im.netty.server.IServer;
import com.msb.im.netty.server.ServerAsClientAble;
import com.msb.im.netty.server.WebSocketServer;
import com.msb.im.nacos.ImNacosService;
import com.msb.im.redis.RedisService;
import com.msb.im.redis.SystemRedisService;
import com.msb.im.service.IIdService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * WebSocket服务器初始化器
 * 负责WebSocket服务器的启动、配置和集群管理
 * 实现CommandLineRunner接口，在Spring Boot应用启动后自动执行初始化逻辑
 *
 * 主要功能：
 * 1. 创建和配置WebSocket服务器实例
 * 2. 初始化缓存数据（消息ID、会话ID等）
 * 3. 服务注册与发现（基于Nacos）
 * 4. 集群服务器间的连接管理
 * 5. 定时监听和连接新的服务器节点
 *
 * @description: WebSocket服务器启动和集群管理的核心配置类
 * @author: zhou miao
 * @create: 2022/04/14
 */
@Configuration
@Slf4j
@EnableConfigurationProperties({ImServerProperties.class})
public class WsServerInitializer implements CommandLineRunner {

    /**
     * 服务器Channel初始化器，用于配置新连接的处理管道
     * 负责设置WebSocket连接的各种处理器（编解码、心跳、消息处理等）
     */
    @Autowired
    private ServerChannelInitializer serverChannelInitializer;

    /**
     * Nacos服务注册与发现客户端
     * 用于将当前服务器注册到Nacos，并发现其他服务器节点
     */
    @Autowired
    private ImNacosService nacosService;

    /**
     * 定时任务线程池
     * 用于执行定时任务，如定期检查新的服务器节点、心跳检测等
     */
    @Autowired
    private ScheduledThreadPoolExecutor scheduledThreadPoolExecutor;

    /**
     * IM服务器配置属性
     * 包含端口、线程数、超时时间等服务器配置信息
     */
    @Autowired
    private ImServerProperties imServerProperties;

    /**
     * Redis服务客户端
     * 用于管理服务器集群信息、用户连接信息、消息缓存等
     */
    @Autowired
    private RedisService redisService;

    /**
     * ID生成服务
     * 负责生成消息ID、会话ID、用户会话ID等唯一标识
     */
    @Resource
    private IIdService iIdService;

    /**
     * 系统Redis服务
     * 负责第三方系统配置的缓存管理
     */
    @Resource
    private SystemRedisService systemRedisService;

    /**
     * 商铺服务
     * 处理商铺相关的业务逻辑（暂未在当前代码中使用）
     */
    @Resource
    private StoreService storeService;


    /**
     * 创建WebSocket服务器Bean
     * 使用Spring的@Bean注解将WebSocket服务器注册为Spring容器管理的Bean
     *
     * @return WebSocket服务器实例，实现了IServer接口
     * @throws UnknownHostException 当无法获取本机IP地址时抛出异常
     */
    @Bean
    public IServer webSocketServer() throws UnknownHostException {
        return new WebSocketServer(serverChannelInitializer, imServerProperties, scheduledThreadPoolExecutor);
    }

    /**
     * Spring Boot应用启动后的回调方法
     * 实现CommandLineRunner接口，在应用启动完成后自动执行
     * 按顺序执行WebSocket服务器初始化和缓存初始化
     *
     * @param args 命令行参数（通常不使用）
     * @throws Exception 初始化过程中可能抛出的异常
     */
    @Override
    public void run(String... args) throws Exception {
        // 初始化WebSocket服务器并启动集群管理
        initWebSocketServer();
        // 初始化各种缓存数据
        initCache();
    }

    /**
     * 初始化缓存数据
     * 从数据库加载必要的数据到Redis缓存中，提高系统性能
     * 包括：消息ID、会话ID、用户会话ID、第三方系统配置等
     */
    private void initCache() {
        // 从数据库加载最大消息ID到缓存，用于消息ID的生成
        iIdService.loadDbMaxMessageId2Cache();
        // 从数据库加载最大会话ID到缓存，用于会话ID的生成
        iIdService.loadDbMaxSessionId2Cache();
        // 从数据库加载最大用户会话ID到缓存，用于用户会话ID的生成
        iIdService.loadDbMaxSessionUserId2Cache();
        // 从数据库加载第三方系统配置到缓存，用于系统间的集成
        systemRedisService.loadThirdSystemConfig2Cache();
    }

    /**
     * 初始化WebSocket服务器
     * 启动WebSocket服务器并配置集群管理功能
     * 包括连接已有服务器、注册到Nacos、定时发现新服务器等
     *
     * @throws UnknownHostException 当无法获取本机IP地址时抛出异常
     */
    private void initWebSocketServer() throws UnknownHostException {
        // 获取WebSocket服务器实例
        IServer webSocketServer = webSocketServer();

        // 定义服务器启动成功后的回调逻辑
        Runnable startedCallback = () -> {
            try {
                // 1. 连接Redis中已存在的其他服务器
                List<WebSocketServer> servers = redisService.allNettyServer();
                log.info("作为客户端连接redis中保存的其他服务器 {}", servers);
                if (!servers.isEmpty()) {
                    for (WebSocketServer server : servers) {
                        // 跳过自己，避免自连接
                        if (Objects.equals(webSocketServer.getPort(), server.getPort()) && Objects.equals(webSocketServer.getIp(), server.getIp())) {
                            continue;
                        }
                        // 作为客户端连接其他服务器，实现服务器间通信
                        ((ServerAsClientAble) webSocketServer).connectServer(webSocketServer, server.getIp(), server.getPort());
                    }
                }

                // 2. 将当前服务器注册到Nacos和Redis
                log.info("将netty服务注册到nacos中 同时将当前netty服务添加到redis中 {}", servers);
                // 注册成功后的回调：将服务器信息添加到Redis
                Runnable callback = () -> redisService.addNettyServer(webSocketServer.getIp(), webSocketServer.getPort());
                // 向Nacos注册当前服务器实例
                nacosService.registerInstance(imServerProperties.getNacosNettyName(), webSocketServer.getIp(), imServerProperties.getPort(), "DEFAULT", callback);

                // 3. 启动定时任务：监听Nacos中的新服务器并建立连接
                // 定时轮询Nacos，监听是否有新的服务器注册上来，如有则进行连接
                scheduledThreadPoolExecutor.scheduleWithFixedDelay(() -> {
                    try {
                        // 从Nacos获取所有服务器实例
                        List<Instance> instances = nacosService.getAllInstances(imServerProperties.getNacosNettyName());
                        log.debug("定时获取到nacos上的netty服务 {}", instances);

                        for (Instance instance : instances) {
                            // 将新发现的服务器添加到Redis（如果不存在）
                            redisService.addNettyServerIfNotExist(instance.getIp(), instance.getPort());

                            // 跳过自己，避免自连接
                            if (Objects.equals(webSocketServer.getPort(), instance.getPort()) && Objects.equals(webSocketServer.getIp(), instance.getIp())) {
                                continue;
                            }

                            // 连接新发现的服务器
                            ((ServerAsClientAble) webSocketServer).connectServer(webSocketServer, instance.getIp(), instance.getPort());
                        }

                    } catch (NacosException e) {
                        // Nacos异常处理（当前为空实现，可根据需要添加日志或重试逻辑）
                    }
                }, 0, imServerProperties.getPullNewNettySeconds(), TimeUnit.SECONDS);

            } catch (NacosException e) {
                // 服务注册失败时的处理
                log.error("启动失败 {}", e.getMessage());
                // 关闭线程池，避免资源泄露
                scheduledThreadPoolExecutor.shutdown();
            }
        };

        // 启动WebSocket服务器，并传入启动成功回调
        webSocketServer.start(startedCallback);
    }
}
